#include <iostream>
#include <cassert>
#include <cstring>
#include <string>
#include <ctime>
#include <unistd.h>
#include <pthread.h>
#include <cstdio>

using namespace std;

// pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER; // pthread_mutex_t 就是原生线程库提供的一个数据类型


int tickets = 1000;// 在并发访问的时候，导致了我们数据不一致的问题！临界资源
#define THTREAD_NUM 5

class ThreadData
{
public:
    ThreadData(const string &name, pthread_mutex_t *mtx)
        : name_(name), mtx_(mtx)
    {}

public:
    string name_;
    pthread_mutex_t *mtx_;
};

void *gettickets(void *args)
{
    ThreadData *td = (ThreadData *)args;
    while (true)
    {
        // 抢票逻辑
        int n = pthread_mutex_lock(td->mtx_);
        assert(n == 0);
         // 临界区
        if (tickets > 0)// 1. 判断的本质也是计算的一种
        {
            usleep(1000);
            printf("%s: %d\n", td->name_.c_str(), tickets);
            tickets--;// 2. 也可能出现问题
            n = pthread_mutex_unlock(td->mtx_);
            assert(n == 0);
        }
        else
        {
            n = pthread_mutex_unlock(td->mtx_);
            assert(n == 0);
            break;
        }

        // 抢完票，其实还需要后续的动作
        usleep(rand() % 2000);
    }

    delete td;//防止内存泄漏
    return nullptr;
}

int main()
{
    srand((unsigned long)time(nullptr) ^ getppid() ^ 0666);

    pthread_mutex_t mtx;
    pthread_mutex_init(&mtx, nullptr);
    pthread_t t[THTREAD_NUM];

    for (int i = 0; i < THTREAD_NUM; i++)
    {
        string name = "thread ";
        name += to_string(i + 1);
        ThreadData *td = new ThreadData(name, &mtx);
        pthread_create(t + i, nullptr, gettickets, (void *)td);
    }

    for (int i = 0; i < THTREAD_NUM; i++)
    {
        pthread_join(t[i], nullptr);
    }

    pthread_mutex_destroy(&mtx);

    return 0;
}


// 加锁保护：加锁的时候，一定要保证加锁的粒度，越小越好！！
// 加锁就是串行执行了吗？ 是的，执行临界区代码一定是串行的！
//加锁了之后，线程在临界区中，是否会切换，会有问题吗？
//会切换，不会！第一次理解：虽然被切换了，但是你是持有锁被切换的, 所以其他抢票线程要执行临界区代码，
//也必须先申请锁，锁它是无法申请成功的，所以，也不会让其他线程进入临界区，就保证了临界区中数据一致性！
// 要访问临界资源，每一个线程都必须现申请锁，每一个线程都必须先看到同一把锁&&访问它，锁本身是不是就是一种共享资源？
// 谁来保证锁的安全呢？？所以，为了保证锁的安全，申请和释放锁，必须是 原子的！！！自己保证